﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using Comm.PublicClass;
using System.Runtime.InteropServices;
using Comm.MultiLan;
using System.Reflection;

namespace WsProtocol.IEC62056
{
    //shaunwang 2019-10-16 KMS加密对象类
    [Serializable]
    public class Encryption
    {
        [DllImport(".\\DlmsEncptDll.dll")]
        static extern int Decrypt_StringData(string pKey, string pIV, string pHDR, string pCiphertext, StringBuilder pTag, StringBuilder pOutPlaintext);
        [DllImport(".\\DlmsEncptDll.dll")]
        static extern int Encrypt_StringData(string pKey, string pIV, string pHDR, string pPlaintext, StringBuilder pOutPlaintext, StringBuilder pOutTag);

        [DllImport(".\\DlmsEncptDll.dll")]  //char* pKey,char* pPlain,char* pCipher
        static extern int aes_wrap_String(string pKey, string pPlaintext, StringBuilder pOutPlaintext);

        [DllImport(".\\DlmsEncptDll.dll")]
        static extern int aes_unwrap_string(string pKey, string pCipher, StringBuilder pOutPlaintext);
        
        public enum securityControl
        {
            AUTHENTICATE = 16, 	// 0x10  认证
            ENCRYPT = 32, 	// 0x20      加密
            AUTHENTICATE_AND_ENCRYPT = 48 // 0x30   认证加密
        }



        [Serializable]
        public class EncryptKey  //SecuritySuite为0和1时 Key 16字节
        {
            public string MK { get; set; }
            public string EK { get; set; }
            public string BK { get; set; }
            public string AK { get; set; }
            public string DK { get; set; }
            // public string Rec_IV { get; set; }
        }
        private static int MaxBufferSize = 8 * 1024 * 1024;

        private int KeyLen = 16;
        private int TagLen = 12;

        private string IVector; //Sys-T + FC
        private string m_MeterNo;
        private string m_FactoryCode;
        private UInt32 m_IC;
        private SecuritySuite m_securitySuite; //一般固定为GCM_128

        public string Sys_T{get;set;}   //System Title=FactName+MeterNo.substring(MeterNo.Length-10,10) 5字节
        public RequirContext ContextName { get; set; }
        public Authentication apduAuth { get; set; }
        public SecurityPolicy securityPolicy { get; set; }
        public SecuritySuite securitySuite
        {
            get { return m_securitySuite; }
            set
            {
                m_securitySuite = value;
                switch (m_securitySuite)
                {
                    case SecuritySuite.GCM_128:
                    case SecuritySuite.GCM_128_SHA_256:
                        KeyLen = 16;
                        break;
                    case SecuritySuite.GCM_256_SHA_384:
                        KeyLen = 32;
                        break;

                }
            }
        }
        public EncryptKey Key { get; set; }
        public string MeterNo  //取5个字节 
        {
            get { return m_MeterNo; }
            set
            {

                if (!string.IsNullOrEmpty(value) && value.Length > 10)
                    m_MeterNo = value.Substring(value.Length - 10, 10);
                else
                    m_MeterNo = value.PadLeft(10, '0');              
            }

        }
        public string FactoryCode  //取3个字节
        {
            get { return m_FactoryCode; }
            set { }
        }
        public UInt32 IC   //4字节,帧计数器double_long_unsigned
        {
            get { 
                return m_IC;
            }
            set
            {
                m_IC = value;              
            }
        }

        public int Key_Set { get; set; }
        public bool NeedResponse = true;

        public string PlaintData;    //明文数据 
        public string EncryptData;  //密文数据
        public string TagStr;  //认证信息 即MAC

        public string CtoS { get; set; } //hxh 高级认证连接时第一步发送给表的随机数
        public string StoC { get; set; } // 高级认证连接后返回的数据加密后得到，需由64类的方法1发送给表
        public string AuthCtoS { get; set; } //由64类的方法1返回的数据再加密后得到的
        public Encryption()
        {
            ContextName = RequirContext.LN_NoCipher;
            apduAuth = Authentication.LLS;
            securityPolicy = SecurityPolicy.无认证;
            m_securitySuite = SecuritySuite.GCM_128;
            m_FactoryCode = "WSE";            
            Key_Set = 0;
            Key = new EncryptKey();
        }

        //复制自身
        public Encryption Copy()
        {
            //新建一个
            Encryption newmeter = new Encryption();
            //复制属性
            PropertyInfo[] fromFields = typeof(Encryption).GetProperties();
            PubFunction.SetProperties(fromFields, this, newmeter);
            return newmeter;
        }

        public bool IfEncrypt() {
            try
            {
                if ((ContextName == RequirContext.LN_Cipher || apduAuth >= Authentication.HLS)
                            && securityPolicy != SecurityPolicy.无认证)
                    return true;
                else
                    return false;
            }
            catch(Exception ex ){
                return false;
            }
        }

        public string GetSysTitle() {
            return  MyConvert.StrToHex(m_FactoryCode.Substring(0, 3)) + m_MeterNo;           
        }

        private string GetSControl(SecurityPolicy sPolicy, bool Keyset)
        {
            // Bit 7 	    Bit 6 	  Bit 5 Bit 4 	Bit 3..0 
            //Compression 	Key_Set 	E 	  A 	Security_Suite_Id=0000
            int keyset = Keyset ? Key_Set : 0; //进入加密参数时不用考虑Key_Set
            string sContr = keyset.ToString() + MyConvert.DecToBin(((int)sPolicy).ToString(), 2) + "0000";
            return MyConvert.BinToHex(sContr);
        }      


        private string GetFormatKey(string sKey)
        {

            String KeyStr = sKey;
            if (string.IsNullOrEmpty(sKey))
            {
                KeyStr = this.Key.EK;
                if (!NeedResponse || Key_Set==1)
                    KeyStr = this.Key.BK;
                if (!string.IsNullOrEmpty(this.Key.DK))
                    KeyStr = this.Key.DK;              

            }
            if (KeyStr.Length != KeyLen * 2)
                KeyStr = KeyStr.Length > KeyLen * 2 ? KeyStr.Substring(KeyStr.Length - KeyLen * 2, KeyLen * 2) : KeyStr.PadLeft(KeyLen * 2, '0');

            return KeyStr;
        }

        public string GCM_AESEncrypt(SecurityPolicy sPolicy, bool ICIncrease, ref string errorinfo) //固定用Authen方式加密
        {        
          try
          {
              return GCM_EncryptByKeyStr(sPolicy, "", ICIncrease, ref  errorinfo);

          }
          catch (Exception ex)
          {
              errorinfo = ex.ToString();
              return string.Empty;
          }
        }

        //public string GCM_EncryptByKeyStr(SecurityPolicy sPolicy, string EkKeyStr, bool ICIncrease, ref string errorinfo) //固定用Authen方式加密
        //{
        //   return  GCM_EncryptByKeyStr(sPolicy, EkKeyStr, ICIncrease, true, ref errorinfo);
        //}

        public string GCM_EncryptByKeyStr(SecurityPolicy sPolicy,string EkKeyStr,bool ICIncrease,  ref string errorinfo) //固定用Authen方式加密
        {
            int iresult = 0;
            try
            {

                string KeyStr = GetFormatKey(EkKeyStr);

                this.Key.AK = GetFormatKey(this.Key.AK);
                string ADataStr = GetSControl(sPolicy,false) + this.Key.AK;

                string PlaintText = this.PlaintData;
                if (sPolicy == SecurityPolicy.需认证) //认证模式下的A－A ＝SC + AK+ PlaintData
                {
                    ADataStr = ADataStr + this.PlaintData;
                    PlaintText = string.Empty;
                }

                StringBuilder Cliphertext = new StringBuilder(MaxBufferSize);//加密输出8 * 1024 * 1024
                StringBuilder pOutTag = new StringBuilder(128);//tag

                if (ICIncrease)
                    m_IC++;

                IVector = GetSysTitle() + MyConvert.DecToHex(m_IC.ToString(), 8);

                iresult = Encrypt_StringData(KeyStr, IVector, ADataStr, PlaintText, Cliphertext, pOutTag); //进行加密操作
                if (iresult > 0)
                {
                    if (sPolicy == SecurityPolicy.需加密) //只加密模式无Tag
                        TagStr = "";
                    else
                        TagStr = pOutTag.ToString().ToUpper();
                    if (string.IsNullOrEmpty(Cliphertext.ToString()) && apduAuth != Authentication.HLS_GMAC)
                        Cliphertext.Append(this.PlaintData);


                    switch (sPolicy)
                    {
                        case SecurityPolicy.需认证:
                            if (ICIncrease)
                                EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + this.PlaintData + TagStr;
                            else  //建连接时最后一步要用
                                EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + TagStr;
                            break;
                        case SecurityPolicy.需加密:
                        case SecurityPolicy.需认证加密:
                            EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + Cliphertext.ToString().ToUpper() + TagStr;
                            break;

                    }
                    
                }
                else
                    EncryptData = string.Empty;                

                return EncryptData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public string GCM_AESEncrypt(ref string errorinfo)
        {
            return GCM_AESEncrypt(securityPolicy,true,ref  errorinfo);           

        }

        public string GCM_AESDecrypt(int EKFlag, ref string errorinfo)
        {
            errorinfo = string.Empty;
            try
            {
                string KeyStr = EKFlag == 0 ? Key.EK : Key.DK;
                KeyStr = GetFormatKey(KeyStr);

                this.Key.AK = GetFormatKey(this.Key.AK);                     

                string Valuestr = this.EncryptData;
                SecurityPolicy sPolicy = (SecurityPolicy)int.Parse(Valuestr.Substring(0, 1));
                Valuestr = Valuestr.Substring(2); //去掉加密模式标志               
                string ADataStr = GetSControl(sPolicy, false) + this.Key.AK;

                string IVector = Sys_T + Valuestr.Substring(0, 8); //以本次返回的 Sys_T和FC去解密               

                Valuestr = Valuestr.Substring(8); //去掉FC字节               
               
                //取得TAG
                StringBuilder mTagStr = new StringBuilder(128);
                mTagStr.Append(this.TagStr);
                if (sPolicy == SecurityPolicy.需认证 || sPolicy == SecurityPolicy.需认证加密)
                {
                    TagStr = Valuestr.Substring(Valuestr.Length - TagLen * 2, TagLen * 2);
                    Valuestr = Valuestr.Substring(0, Valuestr.Length - TagLen * 2);
                    EncryptData = Valuestr;
                }

                if (securityPolicy == SecurityPolicy.需认证) //认证模式下的A－A ＝SC + AK+ PlaintData
                {
                    ADataStr += EncryptData;
                    Valuestr = "";
                }
                StringBuilder Plaintext = new StringBuilder(MaxBufferSize);//解密输出  8 * 1024 * 1024
                int iresult = Decrypt_StringData(KeyStr, IVector, ADataStr, Valuestr, mTagStr, Plaintext); //进行解密操作
                if (iresult > 0)
                {
                    //if (!string.IsNullOrEmpty(TagStr) && string.Compare(TagStr, mTagStr.ToString().ToUpper()) != 0)
                    //    errorinfo = clsLan.GetLan("数据验证失败");

                    PlaintData = Plaintext.ToString().ToUpper();
                    if (string.IsNullOrEmpty(PlaintData))
                        PlaintData = EncryptData;
                }
                else
                {
                    PlaintData = string.Empty;
                    errorinfo = clsLan.GetLan("数据验证失败"); //shaunwang 2019-9-9
                }

                return PlaintData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        /// <summary>
        /// 密钥加密,即第一次加密Aes算法
        /// </summary>
        /// <param name="sKey"></param>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        public string Aes_wrap_String(string sKey, ref string errorinfo)
        {
            StringBuilder Cliphertext = new StringBuilder(MaxBufferSize);//加密输出
            try
            {
                int iresult = aes_wrap_String(sKey, PlaintData, Cliphertext); //进行加密操作
                if (iresult == 0)
                {
                    EncryptData = Cliphertext.ToString().ToUpper();
                }
                else
                    EncryptData = string.Empty;

                return EncryptData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public string Aes_unwrap_String(string sKey, ref string errorinfo)
        {
            errorinfo = string.Empty;
            try
            {              
                StringBuilder Plaintext = new StringBuilder(MaxBufferSize);//解密输出
                int iresult = aes_unwrap_string(sKey, EncryptData, Plaintext); //进行解密操作
                if (iresult== 0)
                {                  
                    PlaintData = Plaintext.ToString().ToUpper();                   
                }
                else
                {
                    PlaintData = string.Empty;
                    errorinfo = clsLan.GetLan("数据解密失败");
                }
                return PlaintData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

    }


    

}
